package sharpen.xobotos.api.interop;
import static sharpen.core.framework.Environments.my;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import sharpen.core.Sharpen;
import sharpen.core.csharp.CSharpPrinter;
import sharpen.core.csharp.ast.CSCompilationUnit;
import sharpen.core.io.IndentedWriter;
import sharpen.xobotos.api.APIDefinition;
import sharpen.xobotos.api.interop.NativeMethod.Kind;
import sharpen.xobotos.api.interop.glue.CompilationUnit;
import sharpen.xobotos.api.interop.glue.CompilationUnitHeader;
import sharpen.xobotos.api.interop.glue.IncludeDirective;
import sharpen.xobotos.api.interop.glue.IncludeSection;
import sharpen.xobotos.api.interop.glue.Node;
import sharpen.xobotos.config.ConfigFile;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class NativeBuilder {
private final NativeConfiguration _config;
private final String _name;
private final String _sourceName;
private final String _headerName;
private final Map<String, NativeMethodBuilder> _nativeMethods = new HashMap<String, NativeMethodBuilder>();
private final List<IncludeFileProvider> _includeProviders = new ArrayList<IncludeFileProvider>();
private final List<AbstractNativeTypeBuilder> _nativeTypes = new ArrayList<AbstractNativeTypeBuilder>();
private final String _functionPrefix;
private final CompilationUnit _unit;
private CompilationUnitHeader _header;
public NativeBuilder(NativeConfiguration config, String name) {
this._config = config;
this._name = name;
this._sourceName = _name + ".cpp";
this._headerName = _name + ".h";
_unit = new CompilationUnit(name);
_unit.getIncludeSection().addIncludes(config);
String prefix = config.getFunctionPrefix();
if (prefix != null)
_functionPrefix = prefix + "_" + name.replace('.', '_');
else
_functionPrefix = name.replace('.', '_');
}
public String getFunctionPrefix() {
return _functionPrefix;
}
public NativeConfiguration getConfig() {
return _config;
}
public IncludeDirective getHeaderInclude() {
return new IncludeDirective(_headerName, false);
}
public CompilationUnit getCompilationUnit() {
return _unit;
}
public void addIncludeProvider(IncludeFileProvider provider) {
_includeProviders.add(provider);
}
public NativeMethodBuilder registerNativeMethod(MethodDeclaration node, NativeMethod template,
NativeHandleBuilder nativeHandle) {
final IMethodBinding binding = node.resolveBinding();
String prefix = _config.getFunctionPrefix();
if (prefix == null)
prefix = "";
String name;
if (template.getKind() == Kind.DESTRUCTOR)
name = "destructor";
else if (template.getName() != null)
name = template.getName();
else
name = binding.getName();
String nativeName = template.getNativeName();
if (nativeName == null)
nativeName = name;
String funcName = String.format("%s_%s_%s", prefix, binding.getDeclaringClass().getName(), name);
if (_nativeMethods.containsKey(funcName)) {
int index = 0;
while (_nativeMethods.containsKey(funcName + "_" + index))
index++;
funcName = funcName + "_" + index;
}
NativeMethodBuilder builder = new NativeMethodBuilder(this, node, template, nativeName, funcName,
nativeHandle);
_nativeMethods.put(funcName, builder);
return builder;
}
public void registerNativeType(AbstractNativeTypeBuilder builder) {
_nativeTypes.add(builder);
}
public boolean build(boolean needsHeader) {
collectIncludes(_unit.getIncludeSection());
if (needsHeader) {
String condName = _name.replace('.', '_').toUpperCase() + "_GLUE_H";
_header = new CompilationUnitHeader(_name, condName);
_header.setIncludeSection(_unit.getIncludeSection());
IncludeSection newSection = new IncludeSection();
newSection.addInclude(getHeaderInclude());
_unit.setIncludeSection(newSection);
}
for (final AbstractNativeTypeBuilder builder : _nativeTypes) {
if (!builder.build())
return false;
}
for (final AbstractNativeTypeBuilder builder : _nativeTypes) {
if (!builder.createNativeType(_unit))
return false;
if (needsHeader) {
if (!builder.createHeader(_header))
return false;
}
}
for (final NativeMethodBuilder builder : _nativeMethods.values()) {
builder.createNativeMethod(_unit);
}
return true;
}
private void collectIncludes(IncludeSection section) {
for (IncludeFileProvider provider : _includeProviders)
section.addIncludes(provider);
for (IncludeFileProvider provider : _nativeTypes)
section.addIncludes(provider);
for (final NativeMethodBuilder builder : _nativeMethods.values()) {
builder.collectIncludes(section);
}
}
public boolean writeOutput() {
if (!printNativeType(_config, _unit, _sourceName))
return false;
if (_header != null) {
if (!printNativeType(_config, _header, _headerName))
return false;
}
return true;
}
private static boolean printNativeType(NativeConfiguration config, Node unit, String fileName) {
final URI projectRoot = my(APIDefinition.class).getProjectRoot();
final String outputDir = my(ConfigFile.class).getSourceInfo().getOutputFolder();
final String outputPath = projectRoot.getPath() + File.separatorChar + outputDir + File.separatorChar
+ config.getOutputDir();
if (!new File(outputPath).exists())
new File(outputPath).mkdirs();
final String fullSourceName = outputPath + File.separatorChar + fileName;
FileWriter output;
try {
output = new FileWriter(fullSourceName);
} catch (IOException e) {
Sharpen.Log(e, "Cannot create native glue file '%s'", fullSourceName);
return false;
}
IndentedWriter writer = new IndentedWriter(output);
Printer printer = new Printer(writer);
unit.accept(printer);
try {
output.close();
} catch (IOException e) {
Sharpen.Log(e, "Cannot create native glue file '%s'", fullSourceName);
return false;
}
return true;
}
public boolean printManagedType(CSCompilationUnit unit) {
final URI projectRoot = my(APIDefinition.class).getProjectRoot();
final String outputDir = my(ConfigFile.class).getSourceInfo().getOutputFolder();
final String outputPath = projectRoot.getPath() + File.separatorChar + outputDir + File.separatorChar
+ _config.getOutputDir();
if (!new File(outputPath).exists())
new File(outputPath).mkdirs();
final String fullSourceName = outputPath + File.separatorChar + _name + ".cs";
FileWriter output;
try {
output = new FileWriter(fullSourceName);
} catch (IOException e) {
Sharpen.Log(e, "Cannot create managed glue file '%s'", fullSourceName);
return false;
}
CSharpPrinter printer = new CSharpPrinter();
printer.setWriter(output);
printer.print(unit);
try {
output.close();
} catch (IOException e) {
Sharpen.Log(e, "Cannot create managed glue file '%s'", fullSourceName);
return false;
}
return true;
}
}